﻿#ifndef __OBJECT_H__
#define __OBJECT_H__
#include <algorithm>
#include <functional>
#include <memory>
#include <memory_resource>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>

class Object
{
public:
    Object()
    {
        Clear();
        m_value_type = OBJECT_TYPE_NULL;
    }
    Object(bool value)
    {
        Clear();
        m_value_bool = value;
        m_value_type = OBJECT_TYPE_BOOL;
    }
    Object(int value)
    {
        Clear();
        m_value_integer = value;
        m_value_type    = OBJECT_TYPE_NUMBER;
    }
    Object(double value)
    {
        Clear();
        m_value_number = value;
        m_value_type   = OBJECT_TYPE_NUMBER;
    }
    Object(const char *value)
    {
        Clear();
        m_value_string = value;
        m_value_type   = OBJECT_TYPE_STRING;
    }
    Object(const std::string &value)
    {
        Clear();
        m_value_string = value;
        m_value_type   = OBJECT_TYPE_STRING;
    }
    Object(const Object &value)
    {
        m_value_type    = value.m_value_type;
        m_value_bool    = value.m_value_bool;
        m_value_integer = value.m_value_integer;
        m_value_number  = value.m_value_number;
        m_value_string  = value.m_value_string;
        m_value_map     = value.m_value_map;
        m_value_array   = value.m_value_array;
    }

    void operator=(std::nullptr_t) { Clear(); }
    void operator=(bool value) { ToBool().m_value_bool = value; }
    void operator=(int value) { ToInteger().m_value_integer = value; }
    void operator=(double value) { ToNumber().m_value_number = value; }
    void operator=(const char *value) { ToString().m_value_string = value; }
    void operator=(const std::string &value) { ToString().m_value_string = value; }

    Object &operator[](const std::string &key) { return GetByKey(key); }
    Object &operator[](const char *key) { return GetByKey(key); }
    Object &operator[](const int index) { return GetByIndex(index); }

    operator bool() { return ToBoolValue(); }
    operator int() { return ToIntegerValue(); }
    operator double() { return ToNumberValue(); }
    operator std::string() { return ToStringValue(); }

    bool ToBoolValue() { IsBool() ? m_value_bool : (!IsNull()); }
    int ToIntegerValue() { return IsInteger() ? m_value_integer : (IsNumber() ? ((int)m_value_number) : 0); }
    double ToNumberValue() { return IsNumber() ? m_value_number : (IsInteger() ? ((double)m_value_integer) : 0.0); }
    std::string ToStringValue()
    {
        if (IsString())
            return m_value_string;
        else if (IsBool())
            return m_value_bool ? "true" : "false";
        else if (IsInteger())
            return std::to_string(m_value_integer);
        else if (IsNumber())
            return std::to_string(m_value_number);
        else
            return "";
    }

    Object &ToNull()
    {
        Clear();
        return *this;
    }

    Object &ToBool()
    {
        auto value = ToBoolValue();
        Clear();
        m_value_bool = value;
        m_value_type = OBJECT_TYPE_BOOL;
        return *this;
    }

    Object &ToInteger()
    {
        auto value = ToIntegerValue();
        Clear();
        m_value_integer = value;
        m_value_type    = OBJECT_TYPE_INTEGER;
        return *this;
    }

    Object &ToNumber()
    {
        auto value = ToNumberValue();
        Clear();
        m_value_number = value;
        m_value_type   = OBJECT_TYPE_NUMBER;
        return *this;
    }
    Object &ToString()
    {
        auto value = ToStringValue();
        Clear();
        m_value_string = value;
        m_value_type   = OBJECT_TYPE_STRING;
        return *this;
    }

    Object &ToMap()
    {
        if (IsMap()) return *this;
        Clear();
        m_value_map  = std::make_shared<std::unordered_map<std::string, Object>>();
        m_value_type = OBJECT_TYPE_MAP;
        return *this;
    }

    Object &ToArray()
    {
        if (IsArray()) return *this;
        Clear();
        m_value_array = std::make_shared<std::unordered_map<int, Object>>();
        m_value_type  = OBJECT_TYPE_ARRAY;
        return *this;
    }

    void SetByKey(const std::string &key, Object value)
    {
        if (m_value_map == nullptr) m_value_map = std::make_shared<std::unordered_map<std::string, Object>>();
        m_value_map->insert(std::make_pair(key, value));
    }

    Object &GetByKey(const std::string &key)
    {
        if (m_value_map == nullptr) m_value_map = std::make_shared<std::unordered_map<std::string, Object>>();
        return (*m_value_map)[key];
    }

    void SetByIndex(int index, Object value)
    {
        if (m_value_array == nullptr) m_value_array = std::make_shared<std::unordered_map<int, Object>>();
        m_value_array->insert(std::make_pair(index, value));
    }

    Object &GetByIndex(int index)
    {
        if (m_value_array == nullptr) m_value_array = std::make_shared<std::unordered_map<int, Object>>();
        return (*m_value_array)[index];
    }

    int Size()
    {
        return IsArray() ? m_value_array->size() : (IsMap() ? m_value_map->size() : 0);
    }

    bool Equal(const Object &object)
    {
        if (m_value_type != object.m_value_type)
            return false;
        else if (m_value_type == OBJECT_TYPE_NULL)
            return true;
        else if (m_value_type == OBJECT_TYPE_BOOL)
            return m_value_bool == object.m_value_bool;
        else if (m_value_type == OBJECT_TYPE_INTEGER)
            return m_value_integer == object.m_value_integer;
        else if (m_value_type == OBJECT_TYPE_NUMBER)
            return std::abs(m_value_number - object.m_value_number) < 0.0001;
        else if (m_value_type == OBJECT_TYPE_STRING)
            return m_value_string == object.m_value_string;
        else if (m_value_type == OBJECT_TYPE_ARRAY)
            return m_value_array == object.m_value_array;
        else if (m_value_type == OBJECT_TYPE_MAP)
            return m_value_map == object.m_value_map;
        else
            return false;
    }

    void Clear()
    {
        m_value_type    = OBJECT_TYPE_NULL;
        m_value_bool    = false;
        m_value_integer = 0;
        m_value_number  = 0;
        m_value_map     = nullptr;
        m_value_array   = nullptr;
        m_value_string.clear();
    }

    inline bool IsNull() { return m_value_type == OBJECT_TYPE_NULL; }
    inline bool IsBool() { return m_value_type == OBJECT_TYPE_BOOL; }
    inline bool IsInteger() { return m_value_type == OBJECT_TYPE_INTEGER; }
    inline bool IsNumber() { return m_value_type == OBJECT_TYPE_NUMBER; }
    inline bool IsString() { return m_value_type == OBJECT_TYPE_STRING; }
    inline bool IsMap() { return m_value_type == OBJECT_TYPE_MAP; }
    inline bool IsArray() { return m_value_type == OBJECT_TYPE_ARRAY; }
    inline void SetBoolValue(bool value) { m_value_bool  = value; }
    inline void SetIntegerValue(int value) { m_value_integer = value; }
    inline void SetNumberValue(double value) { m_value_number = value; }
    inline void SetStringValue(const std::string &value) { m_value_string = value; }
    inline bool GetBoolValue() { return m_value_bool; }
    inline int GetIntegerValue() { return m_value_integer; }
    inline double GetNumberValue() { return m_value_number; }
    inline const std::string& GetStringValue() { return m_value_string; }

    inline void Encode(std::ostream &os) { EncodeToOStream(os); }
    inline void Decode(std::istream &is) { DecodeFromIStream(is); }

public:
    template <typename T>
    T GetByKey(const std::string &key, T default_value)
    {
        if (m_value_map == nullptr) return default_value;
        auto it = m_value_map->find(key);
        if (it == m_value_map->end()) return default_value;
        return it->second;
    }

protected:
    void EncodeToOStream(std::ostream &os)
    {
        os.write((const char *)(&m_value_type), sizeof(m_value_type));

        if (IsBool())
        {
            os.write((const char *)(&m_value_bool), sizeof(m_value_bool));
        }
        else if (IsInteger())
        {
            os.write((const char *)(&m_value_integer), sizeof(m_value_integer));
        }
        else if (IsNumber())
        {
            os.write((const char *)(&m_value_number), sizeof(m_value_number));
        }
        else if (IsString())
        {
            int size = m_value_string.size();
            os.write((const char *)(&size), sizeof(size));
            os.write((const char *)(m_value_string.c_str()), size);
        }
        else if (IsArray())
        {
            int size = m_value_array->size();
            os.write((const char *)(&size), sizeof(size));
            auto end_iter = m_value_array->end();
            for (auto it = m_value_array->begin(); it != end_iter; it++)
            {
                int index = it->first;
                os.write((const char *)(&index), sizeof(index));
                it->second.EncodeToOStream(os);
            }
        }
        else if (IsMap())
        {
            int size = m_value_map->size();
            os.write((const char *)(&size), sizeof(size));
            auto end_iter = m_value_map->end();
            for (auto it = m_value_map->begin(); it != end_iter; it++)
            {
                auto key = it->first;
                int size = key.size();
                os.write((const char *)(&size), sizeof(size));
                os.write(key.c_str(), size);
                it->second.EncodeToOStream(os);
            }
        }
        else
        { /* null value */
        }
    }

    void DecodeFromIStream(std::istream &is)
    {
        is.read((char *)(&m_value_type), sizeof(m_value_type));

        if (IsBool())
        {
            is.read((char *)(&m_value_bool), sizeof(m_value_bool));
        }
        else if (IsInteger())
        {
            is.read((char *)(&m_value_integer), sizeof(m_value_integer));
        }
        else if (IsNumber())
        {
            is.read((char *)(&m_value_number), sizeof(m_value_number));
        }
        else if (IsString())
        {
            int size = 0;
            is.read((char *)(&size), sizeof(size));
            m_value_string.resize(size);
            is.read((char *)(m_value_string.c_str()), size);
        }
        else if (IsArray())
        {
            m_value_array = std::make_shared<std::unordered_map<int, Object>>();

            int array_size = 0;
            is.read((char *)(&array_size), sizeof(array_size));
            for (int i = 0; i < array_size; i++)
            {
                int index     = 0;
                Object value = nullptr;
                is.read((char *)(&index), sizeof(index));
                value.DecodeFromIStream(is);
                m_value_array->insert(std::make_pair(index, value));
            }
        }
        else if (IsMap())
        {
            m_value_map = std::make_shared<std::unordered_map<std::string, Object>>();

            int map_size = 0;
            is.read((char *)(&map_size), sizeof(map_size));
            for (int i = 0; i < map_size; i++)
            {
                std::string key;
                int key_size = 0;
                is.read((char *)(&key_size), sizeof(key_size));
                key.resize(key_size);
                is.read((char *)(key.c_str()), key_size);
                Object value = nullptr;
                value.DecodeFromIStream(is);
                m_value_map->insert(std::make_pair(key, value));
            }
        }
        else
        {
            Clear();
        }
    }

protected:
    // 类型
    int m_value_type;

    // 值
    bool m_value_bool;
    int m_value_integer;
    double m_value_number;
    std::string m_value_string;
    std::shared_ptr<std::unordered_map<std::string, Object>> m_value_map;
    std::shared_ptr<std::unordered_map<int, Object>> m_value_array;

    // 类型常量
    static const int OBJECT_TYPE_NULL    = 0;
    static const int OBJECT_TYPE_BOOL    = 1;
    static const int OBJECT_TYPE_INTEGER = 2;
    static const int OBJECT_TYPE_NUMBER  = 3;
    static const int OBJECT_TYPE_STRING  = 4;
    static const int OBJECT_TYPE_MAP     = 5;
    static const int OBJECT_TYPE_ARRAY   = 6;
};
#endif
